001 /*
002 * Copyright 2005 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.transit;
020
021 import java.io.File;
022 import java.net.URL;
023 import java.net.URI;
024 import java.rmi.RemoteException;
025 import java.rmi.NoSuchObjectException;
026 import java.rmi.server.UnicastRemoteObject;
027 import java.util.EventObject;
028 import java.util.EventListener;
029
030 import net.dpml.transit.info.CacheDirective;
031 import net.dpml.transit.info.ProxyDirective;
032 import net.dpml.transit.info.TransitDirective;
033 import net.dpml.transit.model.CacheModel;
034 import net.dpml.transit.model.ProxyModel;
035 import net.dpml.transit.model.TransitModel;
036 import net.dpml.transit.model.DisposalEvent;
037 import net.dpml.transit.model.DisposalListener;
038 import net.dpml.transit.monitor.LoggingAdapter;
039
040 import net.dpml.util.EventQueue;
041 import net.dpml.util.Logger;
042
043 /**
044 * The DefaultTransitModel class maintains an active configuration of the
045 * Transit system.
046 *
047 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
048 * @version 1.0.1
049 */
050 public class DefaultTransitModel extends DefaultModel implements TransitModel
051 {
052 // ------------------------------------------------------------------------
053 // static
054 // ------------------------------------------------------------------------
055
056 /**
057 * Default configuration url path.
058 */
059 public static final String DEFAULT_PROFILE_PATH = "local:xml:dpml/transit/standard";
060
061 /**
062 * Default configuration url path.
063 */
064 public static final URI DEFAULT_PROFILE_URI = createStaticURI( DEFAULT_PROFILE_PATH );
065
066 /**
067 * System property key used to hold an overriding configuration url.
068 */
069 public static final String PROFILE_KEY = "dpml.transit.profile";
070
071 private static final EventQueue EVENT_QUEUE = new EventQueue( "dpml.transit" );
072
073 /**
074 * Return a model that is restricted to the secure local environment with
075 * no proxy setting or external hosts.
076 * @param logger the logging channel to assign to the model
077 * @return the transit model
078 */
079 public static DefaultTransitModel getSecureModel( Logger logger )
080 {
081 try
082 {
083 TransitDirective directive = new TransitDirective( null, new CacheDirective() );
084 if( logger.isTraceEnabled() )
085 {
086 ClassLoader system = ClassLoader.getSystemClassLoader();
087 int id = System.identityHashCode( system );
088 logger.trace( "system classloader id: " + id );
089 }
090 return new DefaultTransitModel( EVENT_QUEUE, logger, directive );
091 }
092 catch( Exception e )
093 {
094 final String error =
095 "Unexpected error while constructing static secure model.";
096 throw new RuntimeException( error, e );
097 }
098 }
099
100 /**
101 * Resolve the transit configuration using the default resource path
102 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic
103 * default scenario will be returned.
104 *
105 * @return the transit model
106 * @exception Exception if an error occurs during model construction
107 */
108 public static DefaultTransitModel getDefaultModel() throws Exception
109 {
110 return getDefaultModel( "transit" );
111 }
112
113 /**
114 * Resolve the transit configuration using the default resource path
115 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic
116 * default scenario will be returned.
117 *
118 * @param category the logging channel category name
119 * @return the transit model
120 * @exception Exception if an error occurs during model construction
121 */
122 public static DefaultTransitModel getDefaultModel( String category ) throws Exception
123 {
124 LoggingAdapter adapter = new LoggingAdapter( category );
125 return getDefaultModel( adapter );
126 }
127
128 /**
129 * Resolve the transit configuration using the default resource path
130 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic
131 * default scenario will be returned.
132 *
133 * @param logger the logging channel
134 * @return the transit model
135 * @exception Exception if an error occurs during model construction
136 */
137 public static DefaultTransitModel getDefaultModel( Logger logger ) throws Exception
138 {
139 String path = System.getProperty( PROFILE_KEY );
140 if( logger.isTraceEnabled() )
141 {
142 ClassLoader system = ClassLoader.getSystemClassLoader();
143 int id = System.identityHashCode( system );
144 logger.trace( "system classloader id: " + id );
145 }
146 if( null != path )
147 {
148 URL url = Artifact.createArtifact( path ).toURL();
149 TransitBuilder builder = new TransitBuilder( logger );
150 TransitDirective directive = builder.load( url );
151 return new DefaultTransitModel( EVENT_QUEUE, logger, directive );
152 }
153 else
154 {
155 File prefs = Transit.DPML_PREFS;
156 File config = new File( prefs, "dpml/transit/xmls/standard.xml" );
157 if( config.exists() )
158 {
159 URL url = config.toURL();
160 TransitBuilder builder = new TransitBuilder( logger );
161 TransitDirective directive = builder.load( url );
162 return new DefaultTransitModel( EVENT_QUEUE, logger, directive );
163 }
164 else
165 {
166 return getClassicModel( logger );
167 }
168 }
169 }
170
171 // ------------------------------------------------------------------------
172 // state
173 // ------------------------------------------------------------------------
174
175 private final DefaultProxyModel m_proxy;
176 private final DefaultCacheModel m_cache;
177
178 // ------------------------------------------------------------------------
179 // constructor
180 // ------------------------------------------------------------------------
181
182 /**
183 * Creation of a new TransitModel using a supplied configuration
184 * and logging channel. The implementation will construct a proxy
185 * model, layout registry model, cache model, and repository codebase
186 * model using the supplied configuration.
187 *
188 * @param logger the assigned loging channel
189 * @param directive the transit configuration
190 * @exception NullPointerException if the logger or directive arguments are null
191 * @exception RemoteException if a remote exception occurs
192 */
193 public DefaultTransitModel( Logger logger, TransitDirective directive )
194 throws RemoteException, NullPointerException
195 {
196 this( EVENT_QUEUE, logger, directive );
197 }
198
199 /**
200 * Creation of a new TransitModel using a supplied configuration
201 * and logging channel. The implementation will construct a proxy
202 * model, layout registry model, cache model, and repository codebase
203 * model using the supplied configuration.
204 *
205 * @param queue the event queue
206 * @param logger the assigned logging channel
207 * @param directive the transit configuration
208 * @exception NullPointerException if the logger or directive arguments are null
209 * @exception RemoteException if a remote exception occurs
210 */
211 public DefaultTransitModel( EventQueue queue, Logger logger, TransitDirective directive )
212 throws RemoteException, NullPointerException
213 {
214 super( queue, logger );
215
216 if( null == directive )
217 {
218 throw new NullPointerException( "directive" );
219 }
220
221 m_proxy = createProxyModel( directive );
222 m_cache = createCacheModel( directive );
223 }
224
225 // ------------------------------------------------------------------------
226 // TransitModel
227 // ------------------------------------------------------------------------
228
229 /**
230 * Return the proxy configuration model.
231 * @return the proxy model (null if no proxy config defined).
232 */
233 public ProxyModel getProxyModel()
234 {
235 return m_proxy;
236 }
237
238 /**
239 * Return the cache model.
240 * @return the cache model
241 */
242 public CacheModel getCacheModel()
243 {
244 return m_cache;
245 }
246
247 /**
248 * Add a disposal listener to the model.
249 * @param listener the listener to add
250 */
251 public void addDisposalListener( DisposalListener listener )
252 {
253 super.addListener( listener );
254 }
255
256 /**
257 * Remove a disposal listener from the model.
258 * @param listener the listener to remove
259 */
260 public void removeDisposalListener( DisposalListener listener )
261 {
262 super.removeListener( listener );
263 }
264
265 /**
266 * Internal event handler.
267 * @param eventObject the event to handle
268 */
269 public void processEvent( EventObject eventObject )
270 {
271 if( eventObject instanceof DisposalEvent )
272 {
273 DisposalEvent event = (DisposalEvent) eventObject;
274 processDisposalEvent( event );
275 }
276 }
277
278 private void processDisposalEvent( DisposalEvent event )
279 {
280 EventListener[] listeners = super.getEventListeners();
281 for( int i=0; i < listeners.length; i++ )
282 {
283 EventListener listener = listeners[i];
284 if( listener instanceof DisposalListener )
285 {
286 DisposalListener pl = (DisposalListener) listener;
287 try
288 {
289 pl.notifyDisposal( event );
290 }
291 catch( Throwable e )
292 {
293 final String error =
294 "Disposal notification error.";
295 getLogger().error( error, e );
296 }
297 }
298 }
299 }
300
301 // ------------------------------------------------------------------------
302 // impl
303 // ------------------------------------------------------------------------
304
305 Logger getLoggingChannel()
306 {
307 return getLogger();
308 }
309
310 /**
311 * Trigger disposal of the transit model.
312 */
313 public synchronized void dispose()
314 {
315 DisposalEvent event = new DisposalEvent( this );
316 enqueueEvent( event, true );
317 disposeCacheModel();
318 disposeProxyModel();
319 super.dispose();
320 EVENT_QUEUE.terminateDispatchThread();
321 Thread thread = new Terminator( this );
322 thread.start();
323 }
324
325 /**
326 * Internal model terminator.
327 */
328 private class Terminator extends Thread
329 {
330 private final DefaultTransitModel m_model;
331 Terminator( DefaultTransitModel model )
332 {
333 m_model = model;
334 }
335
336 /**
337 * Initiate model retraction from the RMI.
338 */
339 public void run()
340 {
341 try
342 {
343 UnicastRemoteObject.unexportObject( m_model, true );
344 }
345 catch( NoSuchObjectException e )
346 {
347 // ignore
348 }
349 catch( RemoteException e )
350 {
351 e.printStackTrace();
352 }
353 }
354 }
355
356 private synchronized void disposeProxyModel()
357 {
358 if( null == m_proxy )
359 {
360 return;
361 }
362 else
363 {
364 m_proxy.dispose();
365 try
366 {
367 UnicastRemoteObject.unexportObject( m_proxy, true );
368 }
369 catch( NoSuchObjectException e )
370 {
371 // ignore
372 }
373 catch( RemoteException e )
374 {
375 getLogger().warn( "Remote error during proxy reference removal.", e );
376 }
377 }
378 }
379
380 private synchronized void disposeCacheModel()
381 {
382 m_cache.dispose();
383 try
384 {
385 UnicastRemoteObject.unexportObject( m_cache, true );
386 }
387 catch( NoSuchObjectException e )
388 {
389 // ignore
390 }
391 catch( RemoteException e )
392 {
393 getLogger().warn( "Remote error during cache reference removal.", e );
394 }
395 }
396
397 private DefaultProxyModel createProxyModel( final TransitDirective directive )
398 {
399 try
400 {
401 ProxyDirective config = directive.getProxyDirective();
402 if( null == config )
403 {
404 return null;
405 }
406 else
407 {
408 Logger logger = getLogger().getChildLogger( "proxy" );
409 return new DefaultProxyModel( EVENT_QUEUE, logger, config );
410 }
411 }
412 catch( Throwable e )
413 {
414 final String error =
415 "An error occured during construction of the proxy model.";
416 throw new TransitError( error, e );
417 }
418 }
419
420 private DefaultCacheModel createCacheModel( final TransitDirective directive )
421 {
422 try
423 {
424 Logger logger = getLogger().getChildLogger( "cache" );
425 CacheDirective config = directive.getCacheDirective();
426 return new DefaultCacheModel( EVENT_QUEUE, logger, config );
427 }
428 catch( Throwable e )
429 {
430 final String error =
431 "An error occured during construction of the cache model.";
432 throw new TransitError( error, e );
433 }
434 }
435
436 static DefaultTransitModel getBootstrapModel() throws Exception
437 {
438 Logger logger = new LoggingAdapter( "transit" );
439 return getSecureModel( logger );
440 }
441
442 static DefaultTransitModel getClassicModel( Logger logger ) throws Exception
443 {
444 TransitDirective directive = TransitDirective.CLASSIC_PROFILE;
445 return new DefaultTransitModel( EVENT_QUEUE, logger, directive );
446 }
447
448 private static URI createStaticURI( String path )
449 {
450 try
451 {
452 return Artifact.createArtifact( path ).toURI();
453 }
454 catch( Exception e )
455 {
456 return null;
457 }
458 }
459
460 }
461